package ch.uzh.ifi.seal.ssqa.ui.views;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.DefaultListModel;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

import org.eclipse.emf.ecore.EObject;

import prefuse.controls.ControlAdapter;
import prefuse.data.Node;
import prefuse.data.Tree;
import prefuse.util.FontLib;
import prefuse.util.ui.JFastLabel;
import prefuse.util.ui.JSearchPanel;
import prefuse.util.ui.UILib;
import prefuse.visual.VisualItem;
import ch.uzh.ifi.seal.ssqa.model.CClass;
import ch.uzh.ifi.seal.ssqa.model.Component;
import ch.uzh.ifi.seal.ssqa.model.Composite;
import ch.uzh.ifi.seal.ssqa.model.DiagramComponent;
import ch.uzh.ifi.seal.ssqa.model.Module;
import ch.uzh.ifi.seal.ssqa.model.Source;
import ch.uzh.ifi.seal.ssqa.storage.IStorage;
import ch.uzh.ifi.seal.ssqa.storage.StorageResult;
import ch.uzh.ifi.seal.ssqa.storage.TeneoHibernateStorage;

public class DiagramComponentMetrics extends JPanel{
	
	private Tree tree = new Tree();
	
	private final String name = "name";
	private final String path = "path";
	private final String element = "element";
	
	private HashMap<String, Node> nodes = new HashMap<String, Node>();
	private HashMap<String, Node> notHeader  = new HashMap<String, Node>();
	
	private PieChartPanel chartPanel; 
	
	public DiagramComponentMetrics(){
		 this.chartPanel = new PieChartPanel();
	}
	
	public void generatePanelContent(DiagramComponent component){
		this.tree.clear();
		this.calculateGraph(component);
		TreeMapPrefuseComponent treemap = new TreeMapPrefuseComponent(this.tree, this.name);
		
        // create a search panel for the tree map
        JSearchPanel search = treemap.getSearchQuery().createSearchPanel();
        search.setShowResultCount(true);
        search.setBorder(BorderFactory.createEmptyBorder(5,10,4,0));
        search.setFont(FontLib.getFont("Tahoma", Font.BOLD, 11));
        
        final JFastLabel title = new JFastLabel("Source File TreeMap");
        title.setPreferredSize(new Dimension(350, 20));
        title.setMaximumSize(new Dimension(350, 20));
        title.setVerticalAlignment(SwingConstants.BOTTOM);
        title.setBorder(BorderFactory.createEmptyBorder(3,0,0,0));
        title.setFont(FontLib.getFont("Tahoma", Font.BOLD, 11));
        
        final DefaultListModel model = new DefaultListModel();
        JList list = new JList(model);
        list.setFont(FontLib.getFont("Tahoma", Font.BOLD, 11));
        list.setName("Metrics");
        list.setBorder(BorderFactory.createEmptyBorder(5,5,16,0));
        
        treemap.addControlListener(new ControlAdapter() {
        	
        	public void itemPressed(VisualItem item, MouseEvent e){
        		EObject object = (EObject) item.get("element");
        		if(object instanceof Source){
                	Source source = (Source)object;
                	chartPanel.generateComponentPie(source);
        		}
        	}
        	
            public void itemEntered(VisualItem item, MouseEvent e) {
                title.setText(item.getString(name));
                IStorage storage = new TeneoHibernateStorage();
                EObject object = (EObject) item.get("element");
                if(object instanceof Source){
                	Source source = (Source)object;
                	StorageResult result = storage.executeUniqueQuery("from Source where projectpath='"+source.getProjectPath()+"'");
                	source = (Source) result.getResult();
                	model.addElement("Element Type: Source" );
                	model.addElement("Lines of Code: "+source.getLineCount());
                	model.addElement("Cyclomatic Complexity: "+source.getCyclomaticComplexity());
                	model.addElement("Class Count: "+source.getClasses().size());
                	model.addElement("Function Count: "+source.getFunctionNames().size());
                } else if(object instanceof CClass){
                	CClass cClass = (CClass)object;
                	model.addElement("Element Type: Class" );
                	model.addElement("Cyclomatic Complexity: ");
                } else if(object instanceof Module){
                	Module cClass = (Module)object;
                	model.addElement("Element Type: Module" );
                	model.addElement("Cyclomatic Complexity: ");
                }
            }
            public void itemExited(VisualItem item, MouseEvent e) {
                title.setText(null);
                model.clear();
            }
        });
        
        Box box = UILib.getBox(new java.awt.Component[]{title,search}, true, 10, 3, 0);
        box.setMinimumSize(new Dimension(700, 200));
        
        JPanel panel = new JPanel(new BorderLayout());
        
        JPanel legend = new JPanel(new FlowLayout());
        JLabel label = new JLabel (component.getName());
        label.setBackground(Color.yellow);
        label.setForeground(Color.yellow);
        legend.add(label);
        
        panel.add(legend, BorderLayout.NORTH);
        
        panel.add(treemap, BorderLayout.CENTER);
        
        JPanel cont = new JPanel(new BorderLayout());
        cont.add(list, BorderLayout.NORTH);
        cont.add(box, BorderLayout.SOUTH);
        
        cont.setMaximumSize(new Dimension(500,100));
        
        panel.add(cont, BorderLayout.SOUTH);
        
        UILib.setColor(panel, Color.WHITE, Color.GRAY);
		panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
		
		this.setLayout(new GridLayout(1,2));
        this.add(panel);
        this.add(this.chartPanel);
	}
	
	private void calculateGraph(DiagramComponent diagramComponent){
		IStorage storage = new TeneoHibernateStorage();
		StorageResult result = storage.executeListQuery("from Component where diagrammapping = '"+diagramComponent.getIdentifier()+"' ");
		List<Component> components = (List<Component>) result.getListResult();
		
		
		this.tree.addColumn(this.name, String.class);
		this.tree.addColumn(this.path, String.class);
		this.tree.addColumn(this.element, EObject.class);
		
		Node rootNode = this.tree.addRoot();
		rootNode.set(this.name, diagramComponent.getName().trim());
		rootNode.set(this.path, diagramComponent.getIdentifier().trim());
		rootNode.set(this.element, diagramComponent);
		
		List<String> sourcePaths  = diagramComponent.getSourcePaths();
		
		for(Component component : components){
			this.readOutNodes(component);
		}
		
		for(Component component : components){
			if(component instanceof Composite){
				Module module = (Module)component;
				if(module.getChildren() != null && module.getChildren().size()>0){
					for(Component child : module.getChildren()){
						if(child instanceof Composite && this.nodes.get(((Module)child).getProjectPath().trim())!= null){
							this.tree.addEdge(this.nodes.get(module.getProjectPath().trim()), this.nodes.get(((Module)child).getProjectPath().trim()));
						} else {
							this.tree.addEdge(this.nodes.get(module.getProjectPath().trim()), this.nodes.get(((Source)child).getProjectPath().trim()));
						}
					}
				}
				if(sourcePaths.contains(module.getProjectPath().trim())){
					this.tree.addEdge(rootNode, this.nodes.get(module.getProjectPath().trim()));
				}
			} else {
				if(sourcePaths.contains(((Source)component).getProjectPath())){
					this.tree.addEdge(rootNode, this.nodes.get(((Source)component).getProjectPath().trim()));
				}
			}
		}
		
		for(String key : this.notHeader.keySet()){
			String headerKey = key.substring(0, key.lastIndexOf(".")).concat(".h").trim();
			if(this.nodes.containsKey(headerKey) && !sourcePaths.contains(((Source)this.notHeader.get(key).get(this.element)).getProjectPath())){
//				this.tree.addEdge(this.nodes.get(headerKey), this.notHeader.get(key.trim()));
			} else {
				System.out.println("Problem to find the Source file: "+key +" / "+headerKey);
			}
		}
		result.getSession().close();
	}
	
	private void readOutNodes(Component component){
		if(component instanceof Composite){
			this.createCompositeNode((Composite)component);
		} else if (component instanceof Source){
			this.createSourceNodes((Source)component);
		}
	}
	
	private void createCompositeNode(Composite composite){
		// in the present implementation, there are no other composites then modules
		Module module = (Module)composite;
		Node node = this.tree.addNode();
		node.set(this.name, module.getName().trim());
		node.set(this.path, module.getProjectPath().trim());
		node.set(this.element, composite);
		this.nodes.put(module.getProjectPath().trim(), node);
	}
	
	private void createSourceNodes(Source source){
		Node node = this.tree.addNode();
		node.set(this.name, source.getName());
		node.set(this.path, source.getProjectPath().trim());
		node.set(this.element, source);
		this.nodes.put(source.getProjectPath().trim(), node);
		
		if(!source.isHeaderFile()){
			this.notHeader.put(source.getProjectPath().trim(), node);
		}
		
		if(source.getClasses() != null && source.getClasses().size()>0){
			this.createClassNodes(source, node);
		}
	}
	
	private void createClassNodes( Source source, Node node){
		List<CClass> classes = source.getClasses();
		for(CClass clazz : classes){
			Node classNode = this.tree.addNode();
			classNode.set(this.name, clazz.getName().trim());
			classNode.set(this.path, source.getProjectPath().trim()+"#"+clazz.getName().trim());
			classNode.set(this.element, clazz);
			this.tree.addEdge(node, classNode);
		}
	}
}
